package utils

import (
	"github.com/jinzhu/gorm"
	"github.com/kataras/iris/core/errors"
	"math/rand"
	"sort"
	"strconv"
	"tizhi_front/model"
	"tizhi_front/nspel"
)

type TypeScore struct {
	LabelId int64
	Score   float64
}

type TypeScoreSlice []TypeScore

func (t TypeScoreSlice) Len() int {
	return len(t)
}
func (t TypeScoreSlice) Swap(i, j int) {
	t[i], t[j] = t[j], t[i]
}
func (t TypeScoreSlice) Less(i, j int) bool { // 重写 Less() 方法， 从大到小排序
	return t[j].Score < t[i].Score
}

type TypeScoreSort []TypeScore

func (t TypeScoreSort) Len() int {
	return len(t)
}
func (t TypeScoreSort) Swap(i, j int) {
	t[i], t[j] = t[j], t[i]
}
func (t TypeScoreSort) Less(i, j int) bool { // 重写 Less() 方法， 从大到小排序
	return t[j].LabelId < t[i].LabelId
}

type PrescriptionRecommend struct {
	LabelId int64
	Level   string
	Sex     string
	Grade   string
}

func GetPrescriptionList(uid int64) (prescriptions []model.Prescription0, err error) {
	student := model.Student9{}
	err = student.SelectById(uid)
	if err != nil {
		return
	}
	if student.Sex != 1 && student.Sex != 2 {
		err = errors.New("unknown sex " + strconv.Itoa(int(student.Sex)))
		return
	}
	var prescriptionRecommends []PrescriptionRecommend
	err = getItemTypeScore(uid, &student, &prescriptionRecommends)
	if err != nil {
		return
	}
	nspel.Error(prescriptionRecommends)
	prescription := model.Prescription0{}
	for i := 0; i < len(prescriptionRecommends); i++ {
		err = prescription.SelectByLabelIdLevelSexGrade(prescriptionRecommends[i].LabelId, prescriptionRecommends[i].Level, prescriptionRecommends[i].Sex, prescriptionRecommends[i].Grade)
		if err == gorm.ErrRecordNotFound {
			err = nil
			continue
		} else if err != nil {
			return
		}
		prescriptions = append(prescriptions, prescription)
	}
	return
}

func getItemTypeScore(uid int64, student *model.Student9, prescriptionRecommends *[]PrescriptionRecommend) (err error) {
	var scoreType byte
	if student.CurrentGrade == "11" || student.CurrentGrade == "12" {
		scoreType = 1
	} else if student.CurrentGrade == "13" || student.CurrentGrade == "14" {
		scoreType = 2
	} else if student.CurrentGrade == "15" || student.CurrentGrade == "16" {
		scoreType = 3
	} else if student.CurrentGrade == "21" || student.CurrentGrade == "22" || student.CurrentGrade == "23" || student.CurrentGrade == "31" || student.CurrentGrade == "32" || student.CurrentGrade == "33" {
		if student.Sex == 1 {
			scoreType = 4
		} else if student.Sex == 2 {
			scoreType = 5
		}
	} else {
		err = errors.New("unknown grade " + student.CurrentGrade)
		return
	}
	var typeScore map[int64][]float64
	switch scoreType {
	case 1:
		typeScore, err = getPrimary12TypeScore(uid)
		if err != nil {
			return
		}
		break
	case 2:
		typeScore, err = getPrimary34TypeScore(uid)
		if err != nil {
			return
		}
		break
	case 3:
		typeScore, err = getPrimary56TypeScore(uid)
		if err != nil {
			return
		}
		break
	case 4:
		typeScore, err = getHighMaleTypeScore(uid)
		if err != nil {
			return
		}
		break
	case 5:
		typeScore, err = getHighFemaleTypeScore(uid)
		if err != nil {
			return
		}
		break
	}
	getPrescriptionRecommendCondition(&typeScore, student.Sex, student.CurrentGrade, prescriptionRecommends)
	return
}

func getPrimary12TypeScore(uid int64) (typeScore map[int64][]float64, err error) {
	score := model.Score7{}
	err = score.SelectByStudentIdOrderByMeasurementDataDesc(uid)
	if err != nil {
		return
	}
	typeScore = make(map[int64][]float64)
	err = getTypeScore("体重指数", score.BodyMassIndexBasicScore, score.BodyMassIndexLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("肺活量", score.VitalCapacityBasicScore, score.VitalCapacityLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("50米跑", score.FiftyMetersBasicScore, score.FiftyMetersLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("坐位体前屈", score.SitAndReachBasicScore, score.SitAndReachLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("一分钟跳绳", score.OneMinuteRopeSkippingBasicScore, score.OneMinuteRopeSkippingLevel, &typeScore)
	return
}

func getPrimary34TypeScore(uid int64) (typeScore map[int64][]float64, err error) {
	score := model.Score8{}
	err = score.SelectByStudentIdOrderByMeasurementDateDesc(uid)
	if err != nil {
		return
	}
	typeScore = make(map[int64][]float64)
	err = getTypeScore("体重指数", score.BodyMassIndexBasicScore, score.BodyMassIndexLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("肺活量", score.VitalCapacityBasicScore, score.VitalCapacityLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("50米跑", score.FiftyMetersBasicScore, score.FiftyMetersLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("坐位体前屈", score.SitAndReachBasicScore, score.SitAndReachLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("一分钟跳绳", score.OneMinuteRopeSkippingBasicScore, score.OneMinuteRopeSkippingLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("一分钟仰卧起坐", score.OneMinuteSitUpBasicScore, score.OneMinuteSitUpLevel, &typeScore)
	return
}

func getPrimary56TypeScore(uid int64) (typeScore map[int64][]float64, err error) {
	score := model.Score9{}
	err = score.SelectByStudentIdOrderByMeasurementDateDesc(uid)
	if err != nil {
		return
	}
	typeScore = make(map[int64][]float64)
	err = getTypeScore("体重指数", score.BodyMassIndexBasicScore, score.BodyMassIndexLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("肺活量", score.VitalCapacityBasicScore, score.VitalCapacityLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("50米跑", score.FiftyMetersBasicScore, score.FiftyMetersLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("坐位体前屈", score.SitAndReachBasicScore, score.SitAndReachLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("一分钟跳绳", score.OneMinuteRopeSkippingBasicScore, score.OneMinuteRopeSkippingLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("一分钟仰卧起坐", score.OneMinuteSitUpBasicScore, score.OneMinuteSitUpLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("50米x8往返跑", score.ShuttleRunBasicScore, score.ShuttleRunLevel, &typeScore)
	return
}

func getHighMaleTypeScore(uid int64) (typeScore map[int64][]float64, err error) {
	score := model.Score10{}
	err = score.SelectByStudentIdOrderByMeasurementDateDesc(uid)
	if err != nil {
		return
	}
	typeScore = make(map[int64][]float64)
	err = getTypeScore("体重指数", score.BodyMassIndexBasicScore, score.BodyMassIndexLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("肺活量", score.VitalCapacityBasicScore, score.VitalCapacityLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("50米跑", score.FiftyMetersBasicScore, score.FiftyMetersLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("坐位体前屈", score.SitAndReachBasicScore, score.SitAndReachLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("立定跳远", score.StandingLongJumpBasicScore, score.StandingLongJumpLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("引体向上", score.ChinningBasicScore, score.ChinningLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("1000米跑", score.OneThousandMetersBasicScore, score.OneThousandMetersLevel, &typeScore)
	return
}

func getHighFemaleTypeScore(uid int64) (typeScore map[int64][]float64, err error) {
	score := model.Score11{}
	err = score.SelectByStudentIdOrderByMeasurementDateDesc(uid)
	if err != nil {
		return
	}
	typeScore = make(map[int64][]float64)
	err = getTypeScore("体重指数", score.BodyMassIndexBasicScore, score.BodyMassIndexLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("肺活量", score.VitalCapacityBasicScore, score.VitalCapacityLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("50米跑", score.FiftyMetersBasicScore, score.FiftyMetersLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("坐位体前屈", score.SitAndReachBasicScore, score.SitAndReachLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("立定跳远", score.StandingLongJumpBasicScore, score.StandingLongJumpLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("一分钟仰卧起坐", score.OneMinuteSitUpBasicScore, score.OneMinuteSitUpLevel, &typeScore)
	if err != nil {
		return
	}
	err = getTypeScore("800米跑", score.EightHundredMetersBasicScore, score.EightHundredMetersLevel, &typeScore)
	return
}

func getTypeScore(subject string, score float64, level string, typeScore *map[int64][]float64) (err error) {
	physicalFitness := model.PhysicalFitness1{}
	err = physicalFitness.SelectBySubject(subject)
	if err != nil {
		return
	}
	if _, ok := (*typeScore)[physicalFitness.LabelId]; !ok {
		if level != "缺测" {
			(*typeScore)[physicalFitness.LabelId] = []float64{score}
		} else {
			(*typeScore)[physicalFitness.LabelId] = []float64{-1}
		}
	} else {
		if level != "缺测" {
			(*typeScore)[physicalFitness.LabelId] = append((*typeScore)[physicalFitness.LabelId], score)
		} else {
			(*typeScore)[physicalFitness.LabelId] = append((*typeScore)[physicalFitness.LabelId], -1)
		}
	}
	return
}

func getPrescriptionRecommendCondition(typeScores *map[int64][]float64, sex byte, grade string, prescriptionRecommends *[]PrescriptionRecommend) {
	typeScore := make([]TypeScore, len(*typeScores))
	var i = 0
	for k, v := range *typeScores {
		isAllLack := true
		count := float64(0)
		for i := 0; i < len(v); i++ {
			if v[i] != -1 {
				isAllLack = false
			} else {
				v[i] = 0
			}
			count += v[i]
		}
		if isAllLack {
			typeScore[i].LabelId = k
			typeScore[i].Score = -1
		} else {
			typeScore[i].LabelId = k
			typeScore[i].Score = count / float64(len(v))
		}
		i++
	}
	sort.Sort(sort.Reverse(TypeScoreSlice(typeScore)))
	var lessThan70 int
	for i = 0; i < len(typeScore); i++ {
		if typeScore[i].Score >= 70 {
			break
		} else {
			lessThan70++
		}
	}
	var typeScoreResult []TypeScore
	if lessThan70 >= 3 {
		rand := rand.Perm(lessThan70)
		typeScoreResult = make([]TypeScore, 3)
		for i := 0; i < 3; i++ {
			typeScoreResult[i] = typeScore[rand[i]]
		}
	} else {
		typeScoreResult = make([]TypeScore, 2)
		for i := 0; i < 2; i++ {
			typeScoreResult[i] = typeScore[i]
		}
	}
	sort.Sort(sort.Reverse(TypeScoreSort(typeScoreResult)))
	*prescriptionRecommends = make([]PrescriptionRecommend, len(typeScoreResult))
	for i := 0; i < len(*prescriptionRecommends); i++ {
		(*prescriptionRecommends)[i].LabelId = typeScoreResult[i].LabelId
		if typeScoreResult[i].Score == -1 {
			(*prescriptionRecommends)[i].Level = "level_0"
		} else if typeScoreResult[i].Score < 55 {
			(*prescriptionRecommends)[i].Level = "level_1"
		} else if typeScoreResult[i].Score < 65 {
			(*prescriptionRecommends)[i].Level = "level_2"
		} else if typeScoreResult[i].Score < 80 {
			(*prescriptionRecommends)[i].Level = "level_3"
		} else if typeScoreResult[i].Score < 85 {
			(*prescriptionRecommends)[i].Level = "level_4"
		} else if typeScoreResult[i].Score < 95 {
			(*prescriptionRecommends)[i].Level = "level_5"
		} else {
			(*prescriptionRecommends)[i].Level = "level_6"
		}
		if sex == 1 {
			(*prescriptionRecommends)[i].Sex = "male"
		} else if sex == 2 {
			(*prescriptionRecommends)[i].Sex = "female"
		}
		(*prescriptionRecommends)[i].Grade = "grade_" + grade
	}
}
